﻿/*
 * Copyright (c) 1995 The Regents of the University of California.
 * All rights reserved.    
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice and the following
 * two paragraphs appear in all copies of this software.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.    THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 */

/*
 * Portions of this software Copyright (c) 1995 Brown University.
 * All rights reserved.
 * 
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement
 * is hereby granted, provided that the above copyright notice and the
 * following two paragraphs appear in all copies of this software.
 * 
 * IN NO EVENT SHALL BROWN UNIVERSITY BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF BROWN
 * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * BROWN UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE.    THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
 * BASIS, AND BROWN UNIVERSITY HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
 * SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 */

/*
Portions copyright (c) 2012 Eric Lasota

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
using System;
using System.Collections.Generic;

using System.Text;

namespace MPEGnet
{
    internal struct GoP
    {
        public bool drop_flag;                /* Flag indicating dropped frame. */
        public uint tc_hours;                 /* Hour component of time code.   */
        public uint tc_minutes;               /* Minute component of time code. */
        public uint tc_seconds;               /* Second component of time code. */
        public uint tc_pictures;              /* Picture counter of time code.  */
        public bool closed_gop;               /* Indicates no pred. vectors to
					                             previous group of pictures.    */
        public bool broken_link;              /* B frame unable to be decoded.  */
    }

    internal struct Pict
    {
        public uint temp_ref;                 /* Temporal reference.             */
        public uint code_type;                /* Frame type: P, B, I             */
        public uint vbv_delay;                /* Buffer delay.                   */
        public bool full_pel_forw_vector;     /* Forw. vectors specified in full
					                      pixel values flag.              */
        public uint forw_r_size;              /* Used for vector decoding.       */
        public uint forw_f;                   /* Used for vector decoding.       */
        public bool full_pel_back_vector;     /* Back vectors specified in full 
					                      pixel values flag.              */
        public uint back_r_size;              /* Used in decoding.               */
        public uint back_f;                   /* Used in decoding.               */
    }

    internal struct Slice
    {
        public uint vert_pos;                 /* Vertical position of slice. */
        public uint quant_scale;              /* Quantization scale.         */
    }

    internal struct Block
    {
        public DCTBLOCK dct_recon;
        public short dct_dc_y_past;               /* Past lum. dc dct coefficient.   */
        public short dct_dc_cr_past;              /* Past cr dc dct coefficient.     */
        public short dct_dc_cb_past;              /* Past cb dc dct coefficient.     */
    }

    internal struct Macroblock
    {
        public int mb_address;                        /* Macroblock address.              */
        public int past_mb_addr;                      /* Previous mblock address.         */
        public int motion_h_forw_code;                /* Forw. horiz. motion vector code. */
        public uint motion_h_forw_r;                  /* Used in decoding vectors.        */
        public int motion_v_forw_code;                /* Forw. vert. motion vector code.  */
        public uint motion_v_forw_r;                  /* Used in decdoinge vectors.       */
        public int motion_h_back_code;                /* Back horiz. motion vector code.  */
        public uint motion_h_back_r;                  /* Used in decoding vectors.        */
        public int motion_v_back_code;                /* Back vert. motion vector code.   */
        public uint motion_v_back_r;                  /* Used in decoding vectors.        */
        public uint cbp;                              /* Coded block pattern.             */
        public bool mb_intra;                         /* Intracoded mblock flag.          */
        public bool bpict_past_forw;                  /* Past B frame forw. vector flag.  */
        public bool bpict_past_back;                  /* Past B frame back vector flag.   */
        public int past_intra_addr;                   /* Addr of last intracoded mblock.  */
        public int recon_right_for_prev;              /* Past right forw. vector.         */
        public int recon_down_for_prev;               /* Past down forw. vector.          */
        public int recon_right_back_prev;             /* Past right back vector.          */
        public int recon_down_back_prev;              /* Past down back vector.           */
    }

    internal class PictImage
    {
        public byte[] luminance;            /* Luminance plane.   */
        public byte[] Cr;                   /* Cr plane.          */
        public byte[] Cb;                   /* Cb plane.          */
        public short[] mb_qscale;           /* macroblock info    */
        public uint locked;                 /* Lock flag.         */

        public PictImage( VidStream vid_stream, int w, int h )
        {
            /* Create a YV12 image (Y + V + U) */
            this.luminance = new byte[w*h];
            this.Cr = new byte[w*h/4];
            this.Cb = new byte[w*h/4];

            /* Reset locked flag. */
            this.locked = 0;
        }
    }

    internal interface IMpegVideoIO
    {
        MPEGnet.RingBuffer Stream { get; }

        void SetError(string p);
        void ExecuteDisplay(ImageInfo img);
    }

    public partial class VidStream
    {
        const int RING_BUF_SIZE = 5;

        const int NO_VID_STREAM = (-1);
        const int STREAM_UNDERFLOW = (-2);
        const int OK = 1;

        const int PARSE_FATAL = (-100);
        const int SKIP_PICTURE = (-10);
        const int SKIP_TO_START_CODE = (-1);
        const int PARSE_OK = 1;

        internal const int I_TYPE = 1;
        internal const int P_TYPE = 2;
        internal const int B_TYPE = 3;
        internal const int D_TYPE = 4;

        /* Start codes. */

        const uint SEQ_END_CODE = 0x000001b7;
        const uint SEQ_START_CODE = 0x000001b3;
        const uint GOP_START_CODE = 0x000001b8;
        const uint PICTURE_START_CODE = 0x00000100;
        const uint SLICE_MIN_START_CODE = 0x00000101;
        const uint SLICE_MAX_START_CODE = 0x000001af;
        const uint EXT_START_CODE = 0x000001b5;
        const uint USER_START_CODE = 0x000001b2;
        const uint SEQUENCE_ERROR_CODE = 0x000001b4;

        /* Start code search modes */
        const int SEARCH_NONE = 0;	// Don't scan
        const int SEARCH_ANY = 1;	// Exit scan on any code
        const int SEARCH_NEXT_PICTURE = 2;	// Exit on PICTURE_START_CODE, GOP_START_CODE, or SEQ_END_CODE
        const int SEARCH_MACROBLOCK_FINISH = 3;	// Any, but cycle display if not within SLICE_MIN-SLICE_START and not ERROR

        const uint DISPLAY_LOCK = 0x01;
        const uint PAST_LOCK = 0x02;
        const uint FUTURE_LOCK = 0x04;

        /* Number of macroblocks to process in one call to mpegVidRsrc. */

        const int MB_QUANTUM = 40;

        /* Macros used with macroblock address decoding. */
        const int MB_STUFFING = 34;
        const int MB_ESCAPE = 35;

        uint h_size;                         /* Horiz. size in pixels.     */
        uint v_size;                         /* Vert. size in pixels.      */
        public uint mb_height;               /* Vert. size in mblocks.     */
        public uint mb_width;                /* Horiz. size in mblocks.    */
        byte aspect_ratio;                   /* Code for aspect ratio.     */
        byte picture_rate;                   /* Code for picture rate.     */
        uint bit_rate;                       /* Bit rate.                  */
        uint vbv_buffer_size;                /* Minimum buffer size.       */
        bool const_param_flag;               /* Contrained parameter flag. */
        byte[] intra_quant_matrix = new byte[64];            /* Quantization matrix for
						                                        intracoded frames.      */
        byte[] non_intra_quant_matrix = new byte[64];        /* Quanitization matrix for 
						                                        non intracoded frames.  */
        GoP group;                           /* Current group of pict.     */
        Pict picture;                        /* Current picture.           */
        Slice slice;                         /* Current slice.             */
        Macroblock mblock;                   /* Current macroblock.        */
        Block block;                         /* Current block.             */
        int state;                           /* State of decoding.         */
        int bit_offset;                      /* Bit offset in stream.      */
        int buffer_next;                     /* Pointer to next byte in
						                        buffer.                    */
        int buf_length;                      /* Length of remaining buffer.*/
        uint[] buf_start;                    /* Pointer to buffer start.   */
        byte[] buf_preload;                  /* Preload buffer for IO      */

        /* Brown - beginning of added variables that used to be static or global */
        uint max_buf_length;                /* Max length of buffer.      */
        bool film_has_ended;                /* Boolean - film has ended   */
        uint num_left;                      /* from ReadPacket - leftover */
        uint leftover_bytes;                /* from ReadPacket - leftover */
        bool EOF_flag;                      /* stream is EOF              */
        bool Parse_done;                    /* from read_sys              */
        int right_for, down_for;             /* From ReconPMBlock, video.c */
        int right_half_for, down_half_for;
        uint curBits;                       /* current bits               */
        uint nextBits;                      /* next bits                  */
        int matched_depth;                  /* depth of displayed movie   */
        int ditherType;                     /* What type of dithering     */
        int totNumFrames;                   /* Total Number of Frames     */
        double realTimeStart;               /* When did the movie start?  */
        /* Brown - end of added variables */

        PictImage past;                    /* Past predictive frame.         */
        PictImage future;                  /* Future predictive frame.       */
        PictImage current;                 /* Current frame.                 */
        PictImage[] ring = new PictImage[RING_BUF_SIZE];     /* Ring buffer of frames.         */

        /* KR - beginning of added variables */
        double rate_deal;
        int _skipFrame;
        int _jumpFrame;
        double _oneFrameTime;
        IMpegVideoIO _smpeg;
        /* KR - end of added variables */

        /* begining of added variables */
        bool need_frameadjust;
        int current_frame;
        int start_search_mode;

        ThreadedDispatch _dispatch;
        Reconstructor _recon;

        // EJL - Initializer
        static VlcStaticInitializer _tableInit = new VlcStaticInitializer();

        public bool FilmHasEnded { get { return film_has_ended; } }

        public PerfDebugger PDB = new PerfDebugger();
        
        /*
         *--------------------------------------------------------------
         *
         * DoPictureDisplay --
         *
         *    Converts image from Lum, Cr, Cb to colormap space. Puts
         *      image in lum plane. Updates past and future frame
         *      pointers. Dithers image. Sends to display mechanism.
         *
         * Results:
         *    Pict image structure locked if displaying or if frame
         *      is needed as past or future reference.
         *
         * Side effects:
         *    Lum plane pummelled.
         *
         *--------------------------------------------------------------
         */
        void DoPictureDisplay( )
        {
            if (current != null)
            {
                //using(PDB.Trace("Reconstruct"))
                _recon.Reconstruct(_dispatch, current, past, future, (int)picture.code_type, picture.full_pel_forw_vector, picture.full_pel_back_vector);
            }

            /* Update past and future references if needed. */

            if( (picture.code_type == I_TYPE) ||
                (picture.code_type == P_TYPE) )
            {
                if( future == null )
                {
                    future = current;
                    future.locked |= FUTURE_LOCK;
                }
                else
                {
                    if( past != null )
                        past.locked &= ~PAST_LOCK;
                    past = future;
                    past.locked &= ~FUTURE_LOCK;
                    past.locked |= PAST_LOCK;
                    future = current;
                    future.locked |= FUTURE_LOCK;
                    current = past;

                    ExecuteDisplay();
                }
            }
            else
                ExecuteDisplay();
        }



        /*
         *--------------------------------------------------------------
         *
         * ParseSeqHead --
         *
         *      Assumes bit stream is at the begining of the sequence
         *      header start code. Parses off the sequence header.
         *
         * Results:
         *      Fills the vid_stream structure with values derived and
         *      decoded from the sequence header. Allocates the pict image
         *      structures based on the dimensions of the image space
         *      found in the sequence header.
         *
         * Side effects:
         *      Bit stream irreversibly parsed off.
         *
         *--------------------------------------------------------------
         */
        int ParseSeqHead( )
        {
            uint data;
            int i, j;
        
            /* Flush off sequence start code. */

            flush_bits32();

            /* Get horizontal size of image space. */

            data = get_bits12();
            h_size = (data + 15) & 0xFFFFFFF0;

            /* Get vertical size of image space. */

            data = get_bits12();
            v_size = (data + 15) & 0xFFFFFFF0;

            /* Calculate macroblock width and height of image space. */
            mb_width = (h_size + 15) / 16;
            mb_height = (v_size + 15) / 16;

            /* Parse of aspect ratio code. */
            data = get_bits4();
            aspect_ratio = (byte) data;

            /* Parse off picture rate code. */

            data = get_bits4();
            picture_rate = (byte) data;

            /* Parse off bit rate. */
            data = get_bits18();
            bit_rate = data;

            /* Flush marker bit. */

            flush_bits(1);

            /* Parse off vbv buffer size. */

            data = get_bits10();
            vbv_buffer_size = data;

            /* Parse off contrained parameter flag. */
            data = get_bits1();
            const_param_flag = (data != 0);
            /*
             * If intra_quant_matrix_flag set, parse off intra quant matrix values.
             */

            data = get_bits1();
            if (data != 0)
            {
                for (i = 0; i < 64; i++)
                    intra_quant_matrix[video.ZIGZAG[i]] = (byte)get_bits8();
            }
            /*
             * If non intra quant matrix flag set, parse off non intra quant matrix
             * values.
             */

            data = get_bits1();
            if (data != 0) {
                for (i = 0; i < 64; i++)
                    non_intra_quant_matrix[video.ZIGZAG[i]] = (byte)get_bits8();
            }

            _recon = new Reconstructor((int)mb_width, (int)mb_height);

            /* Go to next start code. */

            resynchronize( );
            start_search_mode = SEARCH_ANY;
            return PARSE_OK;
        }


        /*
         *--------------------------------------------------------------
         *
         * ParseGOP --
         *
         *      Parses of group of pictures header from bit stream
         *      associated with vid_stream.
         *
         * Results:
         *      Values in gop header placed into video stream structure.
         *
         * Side effects:
         *      Bit stream irreversibly parsed.
         *
         *--------------------------------------------------------------
         */

        int ParseGOP( )
        {
            uint data;

            /* Flush group of pictures start code. */

            flush_bits32();

            /* Parse off drop frame flag. */

            data = get_bits1();
            group.drop_flag = (data != 0);

            /* Parse off hour component of time code. */
            data = get_bits5();
            group.tc_hours = data;

            /* Parse off minute component of time code. */

            data = get_bits6();
            group.tc_minutes = data;

            /* Flush marker bit. */

            flush_bits(1);

            /* Parse off second component of time code. */

            data = get_bits6();
            group.tc_seconds = data;

            /* Parse off picture count component of time code. */

            data = get_bits6();
            group.tc_pictures = data;

            /* Parse off closed gop and broken link flags. */

            data = get_bits2();
            if (data > 1)
            {
                group.closed_gop = true;
                group.broken_link = (data > 2);
            }
            else
            {
                group.closed_gop = false;
                group.broken_link = (data != 0);
            }

            /* Goto next start code. */

            resynchronize( );
            start_search_mode = SEARCH_ANY;
            return PARSE_OK;
        }


        /*
         *--------------------------------------------------------------
         *
         * ParsePicture --
         *
         *      Parses picture header. Marks picture to be presented
         *      at particular time given a time stamp.
         *
         * Results:
         *      Values from picture header put into video stream structure.
         *
         * Side effects:
         *      Bit stream irreversibly parsed.
         *
         *--------------------------------------------------------------
         */

        int ParsePicture( )
        {
            uint data;
            int i;

            /* Flush header start code. */
            flush_bits32();

            /* This happens if there is a picture code before a sequence start */
            if (ring[0] == null)
            {
                _smpeg.SetError("Warning: picture block before sequence header block\n");
                return SKIP_PICTURE;
            }

            /* Parse off temporal reference. */
            data = get_bits10();
            picture.temp_ref = data;

            /* Parse of picture type. */
            data = get_bits3();
            picture.code_type = data;

            if ((picture.code_type == B_TYPE) &&
                ((future == null) ||
                ((past == null) && !(group.closed_gop))))
                /* According to 2-D.5.1 (p D-18) this is ok, if the refereneces are OK */
                return SKIP_PICTURE;

            if ((picture.code_type == P_TYPE) && (future == null))
                return SKIP_PICTURE;

            /* Parse off vbv buffer delay value. */
            data = get_bits16();
            picture.vbv_delay = data;

            /* If P or B type frame... */

            if ((picture.code_type == P_TYPE) || 
                (picture.code_type == B_TYPE))
            {
                /* Parse off forward vector full pixel flag. */
                data = get_bits1();
                picture.full_pel_forw_vector = (data != 0);

                /* Parse of forw_r_code. */
                data = get_bits3();

                /* Decode forw_r_code into forw_r_size and forw_f. */

                picture.forw_r_size = data - 1;
                picture.forw_f = (uint)(1 << (int)picture.forw_r_size);
            }
            /* If B type frame... */

            if (picture.code_type == B_TYPE)
            {
                /* Parse off back vector full pixel flag. */
                data = get_bits1();
                picture.full_pel_back_vector = (data != 0);

                /* Parse off back_r_code. */
                data = get_bits3();

                /* Decode back_r_code into back_r_size and back_f. */

                picture.back_r_size = data - 1;
                picture.back_f = (uint)(1 << (int)picture.back_r_size);
            }
            /* Get extra bit picture info. */

            skip_extra_bit_info();

            /* Goto next start code. */
            resynchronize( );
            start_search_mode = SEARCH_ANY;

            /* Find a pict image structure in ring buffer not currently locked. */

            i = 0;

            while (ring[i].locked != 0)
            {
                if (++i >= RING_BUF_SIZE)
                {
                    _smpeg.SetError("Fatal error. Ring buffer full.");
                    return PARSE_FATAL;
                }
            }

            /* Set current pict image structure to the one just found in ring. */

            current = ring[i];

            /* Reset past macroblock address field. */

            mblock.past_mb_addr = -1;

            return PARSE_OK;
        }


        /*
         *--------------------------------------------------------------
         *
         * ParseSlice --
         *
         *      Parses off slice header.
         *
         * Results:
         *      Values found in slice header put into video stream structure.
         *
         * Side effects:
         *      Bit stream irreversibly parsed.
         *
         *--------------------------------------------------------------
         */

        int ParseSlice( )
        {
            uint data;

            data = show_bits32();

            if( data < SLICE_MIN_START_CODE || data > SLICE_MAX_START_CODE )
                _smpeg.SetError("Bad seq start!");

            /* Flush slice start code. */
            flush_bits(24);

            /* Parse off slice vertical position. */
            data = get_bits8();
            slice.vert_pos = data;

            /* Parse off quantization scale. */
            data = get_bits5();
            slice.quant_scale = data;

            /* Parse off extra bit slice info. */

            skip_extra_bit_info();

            /* Reset past intrablock address. */

            mblock.past_intra_addr = -2;

            /* Reset previous recon motion vectors. */

            mblock.recon_right_for_prev = 0;
            mblock.recon_down_for_prev = 0;
            mblock.recon_right_back_prev = 0;
            mblock.recon_down_back_prev = 0;

            /* Reset macroblock address. */

            mblock.mb_address = (int)(((slice.vert_pos - 1) * mb_width) - 1);

            /* Reset past dct dc y, cr, and cb values. */
            block.dct_dc_y_past = 1024 << 3;
            block.dct_dc_cr_past = 1024 << 3;
            block.dct_dc_cb_past = 1024 << 3;

            return PARSE_OK;
        }


        /*
         *--------------------------------------------------------------
         *
         * VidStream --
         *
         *        Allocates and initializes a LWMPEG_VidStream structure. Takes
         *            as parameter requested size for buffer length.
         *
         * Results:
         *        A pointer to the new LWMPEG_VidStream structure.
         *
         * Side effects:
         *        None.
         *
         *--------------------------------------------------------------
         */

        byte[] default_intra_matrix =
        {
            8, 16, 19, 22, 26, 27, 29, 34,
            16, 16, 22, 24, 27, 29, 34, 37,
            19, 22, 26, 27, 29, 34, 34, 38,
            22, 22, 26, 27, 29, 34, 37, 40,
            22, 26, 27, 29, 32, 35, 40, 48,
            26, 27, 29, 32, 35, 40, 48, 58,
            26, 27, 29, 34, 38, 46, 56, 69,
            27, 29, 35, 38, 46, 56, 69, 83
        };

        internal VidStream(uint buffer_len, IMpegVideoIO vidIO, ThreadedDispatch dispatch)
        {
            int i, j;

            _dispatch = dispatch;

            /* Check for legal buffer length. */

            if (buffer_len < 4)
                throw new IndexOutOfRangeException("buffer_len < 4");

            /* Make buffer length multiple of 4. */
            buffer_len = (buffer_len + 3) >> 2;

            /* Copy default intra matrix. */
            for (i = 0; i < 64; i++)
                intra_quant_matrix[i] = default_intra_matrix[i];

            /* Initialize non intra quantization matrix. */
            for (i = 0; i < 64; i++)
                non_intra_quant_matrix[i] = 16;

            /* Initialize pointers to image spaces. */

            this.current = this.past = this.future = null;
            for (i = 0; i < RING_BUF_SIZE; i++)
                this.ring[i] = null;

            /* Create buffer. */
            this.buf_start = new uint[buffer_len];
            this.buf_preload = new byte[buffer_len * 4];

            /*
            * Set max_buf_length to one less than actual length to deal with messy
            * data without proper seq. end codes.
            */

            max_buf_length = buffer_len - 1;

            /* Initialize fields that used to be global */
            rate_deal = -1;

            /* Reset everything for start of display */
            ResetVidStream();

            this._smpeg = vidIO;
        }



        /*
         *--------------------------------------------------------------
         *
         * ResetVidStream --
         *
         *        Re-initializes a LWMPEG_VidStream structure. Takes
         *            as parameter a pointer to the LWMPEG_VidStream to reset.
         *
         * Results:
         *        None.
         *
         * Side effects:
         *        None.
         *
         *--------------------------------------------------------------
         */
        public void ResetVidStream()
        {
            int i;

            /* Initialize pointers to image spaces. */
            this.current = this.past = this.future = null;

            /* Initialize rings */
            for (i = 0; i < RING_BUF_SIZE; i++)
            {
                if (this.ring[i] != null)
                    this.ring[i].locked = 0;    /* Unlock */
            }

            /* Initialize bitstream i/o fields. */
            this.bit_offset = 0;
            this.buf_length = 0;
            this.buffer_next = 0;
            this.curBits = 0;

            /* We are at the beginning of the film, so film has not ended */
            this.film_has_ended = false;

            /* Reset number of frames to zero */
            this.totNumFrames = 0;


            /* Fields added by KR for synchronization */
            this._skipFrame = 0;
            this.realTimeStart = 0;

            /* Reset EOF_flag to 0 */
            this.EOF_flag = false;

            this.current_frame = 0;
            this.need_frameadjust = false;
        }

        void InitPictImages(int w, int h)
        {
            int i;

            this.current = this.past = this.future = null;
            for (i = 0; i < RING_BUF_SIZE; i++)
                this.ring[i] = new PictImage(this, w, h);
        }

        uint show_bits32()
        {
            /* Check for underflow. */
            if (buf_length < 2)
                correct_underflow();
            if (bit_offset != 0)
                return curBits | (nextBits >> (32 - bit_offset));
            return curBits;
        }
        
        uint show_bitsX(int num, uint mask, int shift)
        {
            {
                int bO;

                /* Check for underflow. */
                if (buf_length < 2)
                    correct_underflow();
                bO = bit_offset + num;
                if (bO > 32)
                {
                    bO -= 32;
                    return ((curBits & mask) >> shift) |
                        (nextBits >> (shift + (num - bO)));
                }
                else
                {
                    return ((curBits & mask) >> shift);
                }
            }
        }

        uint show_bits1()  { return show_bitsX(1,  0x80000000, 31); }
        uint show_bits2()  { return show_bitsX(2,  0xc0000000, 30); }
        uint show_bits3()  { return show_bitsX(3,  0xe0000000, 29); }
        uint show_bits4()  { return show_bitsX(4,  0xf0000000, 28); }
        uint show_bits5()  { return show_bitsX(5,  0xf8000000, 27); }
        uint show_bits6()  { return show_bitsX(6,  0xfc000000, 26); }
        uint show_bits7()  { return show_bitsX(7,  0xfe000000, 25); }
        uint show_bits8()  { return show_bitsX(8,  0xff000000, 24); }
        uint show_bits9()  { return show_bitsX(9,  0xff800000, 23); }
        uint show_bits10() { return show_bitsX(10, 0xffc00000, 22); }
        uint show_bits11() { return show_bitsX(11, 0xffe00000, 21); }
        uint show_bits12() { return show_bitsX(12, 0xfff00000, 20); }
        uint show_bits13() { return show_bitsX(13, 0xfff80000, 19); }
        uint show_bits14() { return show_bitsX(14, 0xfffc0000, 18); }
        uint show_bits15() { return show_bitsX(15, 0xfffe0000, 17); }
        uint show_bits16() { return show_bitsX(16, 0xffff0000, 16); }
        uint show_bits17() { return show_bitsX(17, 0xffff8000, 15); }
        uint show_bits18() { return show_bitsX(18, 0xffffc000, 14); }
        uint show_bits19() { return show_bitsX(19, 0xffffe000, 13); }
        uint show_bits20() { return show_bitsX(20, 0xfffff000, 12); }
        uint show_bits21() { return show_bitsX(21, 0xfffff800, 11); }
        uint show_bits22() { return show_bitsX(22, 0xfffffc00, 10); }
        uint show_bits23() { return show_bitsX(23, 0xfffffe00,  9); }
        uint show_bits24() { return show_bitsX(24, 0xffffff00,  8); }
        uint show_bits25() { return show_bitsX(25, 0xffffff80,  7); }
        uint show_bits26() { return show_bitsX(26, 0xffffffc0,  6); }
        uint show_bits27() { return show_bitsX(27, 0xffffffe0,  5); }
        uint show_bits28() { return show_bitsX(28, 0xfffffff0,  4); }
        uint show_bits29() { return show_bitsX(29, 0xfffffff8,  3); }
        uint show_bits30() { return show_bitsX(30, 0xfffffffc,  2); }
        uint show_bits31() { return show_bitsX(31, 0xfffffffe,  1); }

        uint get_bitsX(int num, uint mask, int shift)
        {
            /* Check for underflow. */
            if (buf_length < 2)
                correct_underflow();
            bit_offset += num;

            uint result;
            if ((bit_offset & 0x20) != 0)
            {
                bit_offset -= 32;
                buffer_next++;
                buf_length--;
                if (bit_offset != 0)
                    curBits |= (nextBits >> (num - bit_offset));
                result = ((curBits & mask) >> shift);
                curBits = nextBits << bit_offset;
                nextBits = buf_start[buffer_next + 1];
            }
            else
            {
                result = (curBits & mask) >> shift;
                curBits <<= num;
            }
            return result;
        }



        uint get_bits1() { return get_bitsX(1, 0x80000000, 31); }
        uint get_bits2() { return get_bitsX(2, 0xc0000000, 30); }
        uint get_bits3() { return get_bitsX(3, 0xe0000000, 29); }
        uint get_bits4() { return get_bitsX(4, 0xf0000000, 28); }
        uint get_bits5() { return get_bitsX(5, 0xf8000000, 27); }
        uint get_bits6() { return get_bitsX(6, 0xfc000000, 26); }
        uint get_bits7() { return get_bitsX(7, 0xfe000000, 25); }
        uint get_bits8() { return get_bitsX(8, 0xff000000, 24); }
        uint get_bits9() { return get_bitsX(9, 0xff800000, 23); }
        uint get_bits10() { return get_bitsX(10, 0xffc00000, 22); }
        uint get_bits11() { return get_bitsX(11, 0xffe00000, 21); }
        uint get_bits12() { return get_bitsX(12, 0xfff00000, 20); }
        uint get_bits14() { return get_bitsX(14, 0xfffc0000, 18); }
        uint get_bits16() { return get_bitsX(16, 0xffff0000, 16); }
        uint get_bits18() { return get_bitsX(18, 0xffffc000, 14); }
        uint get_bits32() { return get_bitsX(32, 0xffffffff, 0); }

        uint get_bitsn(int num) { return get_bitsX((num), (0xffffffff << (32 - (num))), (32 - (num))); }

        uint show_bitsn(int num) { return show_bitsX((num), (0xffffffff << (32 - (num))), (32 - (num))); }

        void flush_bits32()
        {
            if (buf_length < 2)
                correct_underflow();
            buffer_next++;
            buf_length--;
            curBits = nextBits << bit_offset;
            nextBits = buf_start[buffer_next + 1];
        }

        void flush_bits(int num)
        {
            if (buf_length < 2)
                correct_underflow();
            bit_offset += num;
            if ((bit_offset & 0x20) != 0)
            {
                buf_length--;
                bit_offset -= 32;
                buffer_next++;
                curBits = nextBits << bit_offset;
                nextBits = buf_start[buffer_next + 1];
            }
            else
                curBits <<= num;
        }

        
        bool next_bits( int num, uint mask )
        {
            return mask == show_bitsn(num);
        }

        
        /*
         *--------------------------------------------------------------
         *
         * get_extra_bit_info --
         *
         *	Parses off extra bit info stream. Extra bit info is
         *      indicated by a flag bit set to 1, followed by 8 bits of
         *      data.  This continues until the flag bit is zero.
         *      Assumes that bit stream set to first flag bit in extra
         *      bit info stream.  This is subject to a safety limit
         *      of 1024 bytes.
         *
         * Side effects:
         *	Bit stream irreversibly parsed.
         *
         *--------------------------------------------------------------
         */
        void skip_extra_bit_info( )
        {
            uint data;
            uint safetyLimit = 1000;

            /* Get first flag bit. */
            data = get_bits1();

            /* If flag is false, return NULL pointer (i.e. no extra bit info). */
            if (data == 0)
                return;

            /* While flag bit is true. */

            while (data != 0 && safetyLimit != 0)
            {
                /* Get next 8 bits of data. */
                data = get_bits8();

                /* Get next flag bit. */
                data = get_bits1();

                safetyLimit--;
            }
        }


        
        /*
         *--------------------------------------------------------------
         *
         * correct_underflow --
         *
         *	Called when buffer does not have sufficient data to 
         *      satisfy request for bits.
         *      Calls get_more_data, an application specific routine
         *      required to fill the buffer with more data.
         *
         * Results:
         *      None really.
         *  
         * Side effects:
         *	buf_length and buffer fields may be changed.
         *
         *--------------------------------------------------------------
         */

        void correct_underflow( )
        {
            if (!get_more_data()) {
                _smpeg.SetError("Stream IO error");
                curBits = 0;
                return;
	        }
            else if (buf_length < 1) {
                _smpeg.SetError("No sequence end code");
                film_has_ended = true;
                return;
            }
            curBits = buf_start[buffer_next] << bit_offset;
            nextBits = buf_start[buffer_next + 1];
        }


        /*
         *--------------------------------------------------------------
         *
         * get_more_data --
         *
         *	Called by get_more_data to read in more data from
         *      video MPG files (non-system-layer)
         *
         * Results:
         *	Input buffer updated, buffer length updated.
         *      Returns 1 if data read, 0 if EOF, -1 if error.
         *
         * Side effects:
         *      None.
         *
         *--------------------------------------------------------------
         */

        bool get_more_data()
        {
            if (EOF_flag)
                return false;

            if (buf_length > 0)
                Array.Copy(buf_start, buffer_next, buf_start, 0, buf_length);

            uint bytesRequested = (max_buf_length - (uint)buf_length) * 4;
            uint num_read = _smpeg.Stream.Read(buf_preload, 0, bytesRequested);

            /* Paulo Villegas - 26/1/1993: Correction for 4-byte alignment */
            {
                uint num_read_rounded;
    
                num_read_rounded = 4*(num_read/4);
    
                /* this can happen only if num_read<request; i.e. end of file reached */
                if ( num_read_rounded < num_read )
                { 
 	                num_read_rounded = 4*( num_read/4+1 );

 	                /* fill in with zeros */
 	                for( uint index=num_read; index<num_read_rounded; index++ )
                        buf_preload[index] = 0;

    	            /* advance to the next 4-byte boundary */
 	                num_read = num_read_rounded;
                }
            }
            if (num_read == 0) {
                buffer_next = 0;
    
                /* Make 32 bits after end equal to 0 and 32
                 * bits after that equal to seq end code
                 * in order to prevent messy data from infinite
                 * recursion.
                 */
                buf_start[buf_length] = 0x0;
                buf_start[buf_length+1] = SEQ_END_CODE;
    
                EOF_flag = true;
                return false;
            }
  
            num_read = num_read/4;
  
            //bool needSwap = System.BitConverter.IsLittleEndian;
            for (int i = 0; i < num_read; i++)
            {
                /*
                uint decoded = System.BitConverter.ToUInt32(buf_preload, i * 4);
                if (needSwap)
                    decoded = (
                        ((decoded << 24) & 0xff000000) |
                        ((decoded << 8) & 0xff0000) |
                        ((decoded >> 8) & 0xff00) |
                        ((decoded >> 24) & 0xff)
                        );
                 */
                uint decoded = (uint)buf_preload[i * 4 + 0] << 24;
                decoded |= (uint)buf_preload[i * 4 + 1] << 16;
                decoded |= (uint)buf_preload[i * 4 + 2] << 8;
                decoded |= (uint)buf_preload[i * 4 + 3] << 0;
                buf_start[buf_length++] = decoded;
            }

            buffer_next = 0;
            return true;
        }


        
        /*
         *--------------------------------------------------------------
         *
         * resynchronize --
         *
         *	Bit offset reset to 0.  Should only happen where a start code is expected.
         *
         * Results:
         *	Status code.
         *
         * Side effects:
         *	Bit stream irreversibly parsed.
         *
         *--------------------------------------------------------------
         */
        int resynchronize( )
        {
            int byteoff;

            /* If insufficient buffer length, correct underflow. */
            if (buf_length < 4)
                correct_underflow();

            /* If bit offset not zero, reset and advance buffer pointer. */

            byteoff = bit_offset % 8;

            if (byteoff != 0)
                flush_bits((8-byteoff));

            /* Return underflow error. */
            return STREAM_UNDERFLOW;
        }

        /*
         *--------------------------------------------------------------
         *
         * mpegVidRsrc --
         *
         *            Parses bit stream until MB_QUANTUM number of
         *            macroblocks have been decoded or current slice or
         *            picture ends, whichever comes first. If the start
         *            of a frame is encountered, the frame is time stamped
         *            with the value passed in time_stamp. If the value
         *            passed in buffer is not null, the video stream buffer
         *            is set to buffer and the length of the buffer is
         *            expected in value passed in through length.
         *
         * Results:
         *            A pointer to the video stream structure used.
         *
         * Side effects:
         *            Bit stream is irreversibly parsed. If a picture is completed,
         *            a function is called to display the frame at the correct time.
         *
         *--------------------------------------------------------------
         */

        public bool mpegVidRsrc(bool first)
        {
            uint data;
            int i, status;

            //using (PDB.Trace("mpegVidRsrc"))
            {
                /*
                * If called for the first time, find start code, make sure it is a
                * sequence start code.
                */

                if (first)
                {
                    this.num_left = 0;
                    this.leftover_bytes = 0;
                    this.Parse_done = false;

                    resynchronize();    /* sets curBits */
                    data = show_bits32();
                    if (data != SEQ_START_CODE)
                    {
                        _smpeg.SetError("Invalid sequence in video stream");
                        return false;
                    }
                }

                if (start_search_mode != SEARCH_NONE)
                {
                    //using (PDB.Trace("StartSearch"))
                    {
                        uint limit = (max_buf_length - 400) / 2;

                        resynchronize();

                        data = 0;
                        for (uint n = 0; n < limit; n++)
                        {
                            data = show_bits32();
                            if ((data & 0xffffff00) == 0x100)
                                break;
                            data = get_bits8();
                        }

                        bool searchFailed = false;

                        switch (start_search_mode)
                        {
                            case SEARCH_ANY:
                                if ((data & 0xffffff00) == 0x100)
                                    start_search_mode = SEARCH_NONE;
                                else
                                    searchFailed = true;
                                break;
                            case SEARCH_NEXT_PICTURE:
                                if (data == PICTURE_START_CODE || data == GOP_START_CODE || data == SEQ_END_CODE)
                                    start_search_mode = SEARCH_NONE;
                                else
                                    searchFailed = true;
                                break;
                            case SEARCH_MACROBLOCK_FINISH:
                                if ((data & 0xffffff00) == 0x100)
                                {
                                    if ((data < SLICE_MIN_START_CODE || data > SLICE_MAX_START_CODE) && data != SEQUENCE_ERROR_CODE)
                                        DoPictureDisplay();
                                    start_search_mode = SEARCH_NONE;
                                }
                                else
                                    searchFailed = true;
                                break;
                        };

                        if (searchFailed)
                            flush_bits32();
                        goto done;
                    }
                }

                /* Get next 32 bits (size of start codes). */

                data = show_bits32();

                /* Check for end of file */
                if (EOF_flag)
                {
                    /* Set ended flag first so that ExecuteDisplay may check it. */

                    film_has_ended = true;

                    if (future != null)
                    {
                        current = future;
                        this.ExecuteDisplay();
                    }

                    goto done;
                }

                /*
                * Process according to start code (or parse macroblock if not a start code
                * at all).
                */

                switch (data)
                {
                    case SEQ_END_CODE:
                    case 0x000001b9:     /*    handle ISO_11172_END_CODE too */
                        flush_bits32();
                        goto done;

                    case SEQ_START_CODE:
                        /* Sequence start code. Parse sequence header. */
                        if (ParseSeqHead() != PARSE_OK)
                        {
                            _smpeg.SetError("Sequence head parse failed");
                            goto error;
                        }

                        if (first)
                            InitPictImages((int)mb_width * 16, (int)v_size * 16);

                        /*
                         * Return after sequence start code so that application above can use
                         * info in header.
                         */
                        goto done;


                    case GOP_START_CODE:
                        /* Group of Pictures start code. Parse gop header. */
                        if (ParseGOP() != PARSE_OK)
                        {
                            _smpeg.SetError("GOP parse failed");
                            goto error;
                        }
                        goto done;

                    case PICTURE_START_CODE:
                        /* Picture start code. Parse picture header and first slice header. */
                        status = ParsePicture();

                        if (status == PARSE_FATAL)
                            goto error;

                        if ((picture.code_type == B_TYPE) && _skipFrame != 0)
                        {
                            status = SKIP_PICTURE;
                            _skipFrame--;
                        }

                        if (current == null)
                            status = SKIP_PICTURE;

                        if (status == SKIP_PICTURE)
                        {
                            resynchronize();
                            start_search_mode = SEARCH_NEXT_PICTURE;

                            goto done;
                        }
                        else if (status != PARSE_OK)
                        {
                            _smpeg.SetError("Picture parse failed");
                            goto error;
                        }

                        // EJL - Just finish, first slice will be parsed on the next call
                        goto done;

                    case SEQUENCE_ERROR_CODE:
                        flush_bits32();
                        resynchronize();
                        start_search_mode = SEARCH_ANY;
                        goto done;

                    default:
                        /* No base picture for decoding */
                        if (current == null)
                        {
                            flush_bits32();
                            resynchronize();
                            start_search_mode = SEARCH_ANY;
                            goto done;
                        }

                        /* Check for slice start code. */

                        if ((data >= SLICE_MIN_START_CODE) && (data <= SLICE_MAX_START_CODE))
                        {
                            /* Slice start code. Parse slice header. */
                            if (ParseSlice() != PARSE_OK)
                            {
                                _smpeg.SetError("Slice parse failed");
                                goto error;
                            }
                        }
                        else
                        {
                            // EJL - It's possible that we encountered a code type we can't handle for some reason so just ignore it
                            if ((data & 0xffffff00) == 0x100)
                            {
                                flush_bits32();
                                resynchronize();
                                start_search_mode = SEARCH_ANY;
                                goto done;
                            }
                        }
                        break;
                }


                /* Parse next MB_QUANTUM macroblocks. */

                //using (PDB.Trace("ParseBlock"))
                for (i = 0; i < MB_QUANTUM; i++)
                {
                    /* Check to see if actually a startcode and not a macroblock. */

                    if (!next_bits(23, 0x00000000) &&
                                !film_has_ended)
                    {
                        /* Not start code. Parse Macroblock. */

                        //using (PDB.Trace("ParseMacroBlock"))
                        {
                            if (ParseMacroBlock() != PARSE_OK)
                            {
                                _smpeg.SetError("Macroblock parse failed");
                                goto error;
                            }
                        }
                    }
                    else
                    {
                        /* Not macroblock, actually start code. Get start code and wind down. */

                        resynchronize();
                        start_search_mode = SEARCH_MACROBLOCK_FINISH;
                        goto done;
                    }
                }


                /* Check if we just finished a picture on the MB_QUANTUMth macroblock */

                if (next_bits(23, 0x00000000))
                {
                    resynchronize();
                    start_search_mode = SEARCH_MACROBLOCK_FINISH;
                }

                goto done;

            error:
                resynchronize();
                start_search_mode = SEARCH_ANY;
                return false;

            done:
                return true;
            }
        }

        void ExecuteDisplay()
        {
            if( _skipFrame != 0 )
            {
                _skipFrame--;
                return;
            }

            uint frameRateNum = 1;
            uint frameRateDenom = 1;

            frameRateDenom = 1;
            switch (picture_rate)
            {
                case 1:
                    frameRateNum = 23976;
                    frameRateDenom = 1000;
                    break;
                case 2:
                    frameRateNum = 24;
                    break;
                case 3:
                    frameRateNum = 25;
                    break;
                case 4:
                    frameRateNum = 2997;
                    frameRateDenom = 100;
                    break;
                case 5:
                    frameRateNum = 30;
                    break;
                case 6:
                    frameRateNum = 50;
                    break;
                case 7:
                    frameRateNum = 5994;
                    frameRateDenom = 100;
                    break;
                case 8:
                    frameRateNum = 60;
                    break;
                default:
                    return; // Forget it
            }
            
            _smpeg.ExecuteDisplay(new ImageInfo(h_size, v_size, mb_width * 16, mb_width * 8, mb_width * 8,
                current.luminance, current.Cb, current.Cr, frameRateNum, frameRateDenom));
        }


        internal void SetFrameSkip(int numFrames)
        {
            _skipFrame = numFrames;
        }
    }

    public struct ImageInfo
    {
        public readonly uint width;
        public readonly uint height;

        public readonly uint yPitch;
        public readonly uint cbPitch;
        public readonly uint crPitch;

        public readonly byte[] yPlane;
        public readonly byte[] cbPlane;
        public readonly byte[] crPlane;

        public readonly uint frameRateNum;
        public readonly uint frameRateDenom;

        public ImageInfo(uint width, uint height, uint yPitch, uint cbPitch, uint crPitch, byte[] yPlane, byte[] cbPlane, byte[] crPlane, uint frameRateNum, uint frameRateDenom)
        {
            this.width = width;
            this.height = height;

            this.yPitch = yPitch;
            this.cbPitch = cbPitch;
            this.crPitch = crPitch;

            this.yPlane = yPlane;
            this.cbPlane = cbPlane;
            this.crPlane = crPlane;

            this.frameRateNum = frameRateNum;
            this.frameRateDenom = frameRateDenom;
        }
    }
}
